import pygame
import os
from math import floor
from random import randint

pygame.init()
pygame.mixer.init()

WIDTH, HEIGHT = 900, 600
MouseX, MouseY = 0, 0
clock = pygame.time.Clock()
FPS = 60

# image loads
player_head_img = os.path.join("./img/player_head.png")
block_img = os.path.join("./img/stone.png")
frame_img = os.path.join("./img/frame.png")

# sound loads
Place = lambda: pygame.mixer.Sound(f"./audio/place/Stone_dig{randint(1, 4)}.ogg")
Break = lambda: pygame.mixer.Sound(f"./audio/break/Stone_dig{randint(1, 4)}.ogg")
Step = lambda: pygame.mixer.Sound(f"./audio/step/Stone_step{randint(1, 4)}.ogg")

screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Minecraft*  Rungame v1.1')
pygame.display.set_icon(pygame.image.load(player_head_img))
screen.fill("#B0C4DE")

Blocks = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
]

BlocksRect = []

all_sprites = pygame.sprite.Group()


def SoundPlay(Sound):
    Sound.play()


def RectNext(Rect1, Rect2):
    if Rect1.right == Rect2.left and Rect1.top < Rect2.bottom and Rect1.bottom > Rect2.top:
        return True
    if Rect1.left == Rect2.right and Rect1.top < Rect2.bottom and Rect1.bottom > Rect2.top:
        return True
    if Rect1.bottom == Rect2.top and Rect1.left < Rect2.right and Rect1.right > Rect2.left:
        return True
    if Rect1.top == Rect2.bottom and Rect1.left < Rect2.right and Rect1.right > Rect2.left:
        return True
    return False


class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load(player_head_img).convert()
        self.rect = self.image.get_rect()
        self.rect.center = (WIDTH / 2, HEIGHT / 2)
        self.y_pos = HEIGHT / 2
        self.x_pos = WIDTH / 2
        self.y_speed = 0
        self.speed = 8
        self.tick, self.ticks = 0, self.speed * 2
        self.ticks = self.speed * 2
        self.LastX = self.LastY = 0
        self.jumping = False
        self.LastJumping = False
        self.suspension = True
        self.LastSuspension = True

    def Contact_edge(self):
        if self.rect.left < 0:
            return True
        if self.rect.right > WIDTH:
            return True
        for block in BlocksRect:
            if self.rect.colliderect(block):
                return True
        return False

    def positioning(self):
        self.rect.center = (self.x_pos, self.y_pos)

    def horizontal(self, x_speed):
        self.x_pos += x_speed
        self.positioning()
        while self.Contact_edge():
            if x_speed > 0:
                self.x_pos -= 1
            else:
                self.x_pos += 1
            self.positioning()

    def vertical(self, y_speed):
        self.y_pos += y_speed
        self.positioning()
        while self.Contact_edge():
            if y_speed > 0:
                self.y_pos -= 1
                self.jumping = False
            else:
                self.y_pos += 1
            self.positioning()
            self.y_speed = 0

    def noise(self):
        self.tick += 1
        for block in BlocksRect:
            if RectNext(self.rect, block) and self.rect.bottom == block.top:
                if self.rect.x != self.LastX:
                    if self.tick == self.ticks:
                        SoundPlay(Step())

    def IsSuspension(self):
        for block in BlocksRect:
            if RectNext(self.rect, block) and self.rect.bottom == block.top:
                return False
        return True

    def update(self):

        self.suspension = self.IsSuspension()
        key_pressed = pygame.key.get_pressed()
        if key_pressed[pygame.K_a]:
            self.horizontal(-self.speed)
            self.noise()
        elif key_pressed[pygame.K_d]:
            self.horizontal(self.speed)
            self.noise()
        else:
            self.tick -= 1
        if key_pressed[pygame.K_w]:
            if not self.jumping and not self.suspension:
                flag = True
                for block in BlocksRect:
                    if RectNext(block, self.rect):
                        if self.rect.top == block.bottom:
                            flag = False
                            break
                if flag:
                    self.y_speed = -19
                    self.jumping = True

        self.y_speed += 1
        self.vertical(self.y_speed)
        self.positioning()
        if not self.suspension and self.LastSuspension:
            SoundPlay(Step())
        elif self.LastY > self.rect.y:
            for block in BlocksRect:
                if RectNext(self.rect, block) and self.rect.bottom == block.top:
                    SoundPlay(Step())

        if self.tick >= self.ticks:
            self.tick = 0
        self.LastX = self.rect.x
        self.LastY = self.rect.y
        self.LastJumping = self.jumping
        self.LastSuspension = self.suspension


player: Player = Player()

all_sprites.add(player)


class Block(pygame.sprite.Sprite):
    def __init__(self, x, y, SoundPlaying):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(block_img).convert()
        self.XPos = x
        self.YPos = y
        self.rect = self.image.get_rect()
        self.rect.left = y * 50
        self.rect.top = x * 50
        if self.rect.colliderect(player.rect):
            self.kill()
            return
        Blocks[self.XPos][self.YPos] = 1
        all_sprites.add(self)
        BlocksRect.append(self.rect)
        if SoundPlaying:
            SoundPlay(Place())

    def update(self):
        if Blocks[self.XPos][self.YPos] == 0:
            SoundPlay(Break())
            all_sprites.remove(self)
            BlocksRect.remove(self)
            self.kill()


class Frame(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(frame_img).convert()
        self.image.set_colorkey("white")
        self.rect = self.image.get_rect()
        all_sprites.add(self)

    def update(self):
        self.rect.x = floor(MouseX / 50) * 50
        self.rect.y = floor(MouseY / 50) * 50


def BuildBlock(XPos, YPos, SoundPlaying):
    global Blocks
    Block(XPos, YPos, SoundPlaying)


def RemoveBlock(XPos, YPos):
    global Blocks
    Blocks[XPos][YPos] = 0


for i in range(12):
    for j in range(18):
        if Blocks[i][j] == 1:
            BuildBlock(i, j, False)

frame = pygame.sprite.Group()
frame_sprite = Frame()
frame.add(frame_sprite)

running = True
while running:
    clock.tick(FPS)
    MouseX, MouseY = pygame.mouse.get_pos()
    events = pygame.event.get()
    for event in events:
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            MousePressed = pygame.mouse.get_pressed()
            if 0 <= MouseX <= WIDTH and 0 <= MouseY <= HEIGHT:
                PosX = floor(MouseX / 50)
                PosY = floor(MouseY / 50)
                if Blocks[PosY][PosX] == 0 and MousePressed[2]:
                    BuildBlock(PosY, PosX, True)
                elif MousePressed[0]:
                    RemoveBlock(PosY, PosX)

    screen.fill("#B0C4DE")

    all_sprites.draw(screen)
    all_sprites.update()
    frame.draw(screen)
    frame.update()

    pygame.display.update()

pygame.quit()
